﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using EntreeServer.Module;
using EntreeServer.Module.ActionTray;
using EntreeServer.Module.BasicPluginActions;
using EntreeServer.Module.Configuration;
using EntreeServer.Module.Controls;
using EntreeServer.Module.Utility;
using Microsoft.SharePoint;

namespace ME.SDK.SampleApplication
{
    [Guid("3e7772c2-d38c-4cf5-8a9e-694e01937cc4")]
    public class ListSearchApp : EntreeApplication
    {
        #region constants
        private const string PropertyKeySearchQuery = "SearchQuery";
        private const string PropertyKeyType = "Type";
        private const string ScopeTypeRenderItemDetails = "RenderItemDetails";
        private const string ScopeTypeReloadApplication = "ReloadApplication";
        private const string ScopeTypeLoadApplication = "LoadApplication";
        #endregion

        #region EntreeApplication Members
        /// <summary>
        /// Path to be scanned by framework for ListStyles.xml and UserAgentMapping.xml
        /// </summary>
        public override string PluginPath
        {
            get
            {
                return "/_layouts/images/ME.SDK.SampleApplication/ME SDK Sample Application";
            }
        }
        /// <summary>
        /// Default title of a new instance of this app
        /// </summary>
        public override string Title
        {
            get { return "List Search App"; }
        }
        /// <summary>
        /// Default description of a new instance of the app
        /// </summary>
        public override string Description
        {
            get
            {
                return "Allows searching a list using any number of fields.  Also allows to create and edit list items in the configured list.";
            }
        }

        public override void QueryList(EntreeScopeObject scope, EntreeListObject output)
        {
            if (output.PanelCount == 1 && output.ActionScope.TransferToApplication) //for multi panel interface this is handled in BuildPanelsByScope
            {
                //transfering back from new/edit action item - ActionScope will contain info set by the action
                output.ActionScope.Properties.CopyTo(scope.Properties);
                if (output.ActionScope.ScopeType == EntreeServer.Module.ActionTray.ScopeTypes.SharePoint.SPListItem_New_Update || output.ActionScope.ScopeType == EntreeServer.Module.ActionTray.ScopeTypes.SharePoint.SPListItem_Delete)
                {
                    //remove the listitemid from scope and viewstate - 
                    scope.Properties.Remove(EntreeServer.Module.ActionTray.ScopeProperties.SharePoint.SPListItemID);
                    this.ViewState.Remove(EntreeServer.Module.ActionTray.ScopeProperties.SharePoint.SPListItemID);
                    //set type to reload
                    scope.Properties[PropertyKeyType] = ScopeTypeReloadApplication;
                }
            }

            if (scope.Properties.Contains(PropertyKeyType, ScopeTypeLoadApplication) || scope.Properties.Contains(PropertyKeyType, ScopeTypeReloadApplication))
            {
                //get the list using values from the config
                var list = SPContext.Current.Web.Lists[this.Config.ListId];

                //render search screen
                output.ListTitle = "Search {0}".FormatString(list.Title);

                //add home action to the homebar
                output.HomeBar.ActionItems.Items.Add(new HomeActionItem());

                //add search form controls
                var searchForm = output.AddListGroup<EntreeButtonGroup>();
                var searchBox = searchForm.AddFormItem<EntreeTextBox>(PropertyKeySearchQuery);
                searchBox.Name = "Search";
                var searchButton = searchForm.AddListItem<EntreeListItemTitleIcon>();
                searchButton.Title = "Search";
                searchButton.Icon = "{0}/ico-search-16.png".FormatString(this.PluginPath);

                //add group for results
                var resultsGroup = output.AddListGroup();

                //set ClickAction targeting the results group
                searchButton.ClickAction = new EntreeActiveUpdateObject(this)
                {
                    UpdateTarget = resultsGroup,
                    UpdateType = EntreeActiveUpdateObject.EntreeActiveUpdateType.Replace,
                    FormSubmit = true,
                    OverridePanelTargetSelf = true
                };

                if (this.ViewState.ContainsKey(PropertyKeySearchQuery) && !string.IsNullOrEmpty(this.ViewState[PropertyKeySearchQuery].ToString()))
                {
                    searchBox.Value = this.ViewState[PropertyKeySearchQuery].ToString();
                    //render search results using saved query info
                    var itemId = 0;
                    if (this.ViewState.ContainsKey(EntreeServer.Module.ActionTray.ScopeProperties.SharePoint.SPListItemID)) itemId = Convert.ToInt32(this.ViewState[EntreeServer.Module.ActionTray.ScopeProperties.SharePoint.SPListItemID]);
                    renderResults(resultsGroup, this.ViewState[PropertyKeySearchQuery].ToString(), itemId, output.PanelCount > 1);
                }

                //set action scope properties - use SPView ScopeType to pull in action items which support this scope i.e. 'New Item'
                setActionScopeProperties(output.ActionScope, EntreeServer.Module.ActionTray.ScopeTypes.SharePoint.SPView, 0);
            }
            else if (scope.Properties.Contains(PropertyKeyType, ScopeTypeRenderItemDetails))
            {
                output.ListTitle = "Item Details";
                //get the list, view and item using values from the config
                var list = SPContext.Current.Web.Lists[this.Config.ListId];
                var view = list.Views[this.Config.ViewId];
                SPListItem item = null;
                try
                {
                    item = list.GetItemById(int.Parse(scope.Properties[EntreeServer.Module.ActionTray.ScopeProperties.SharePoint.SPListItemID]));
                }
                catch (ArgumentException)
                {
                    item = null;
                }

                if (item == null)
                {
                    //item was deleted
                    if (output.PanelCount < 2)
                    {
                        //render search screen
                        scope.Properties[PropertyKeyType] = ScopeTypeReloadApplication;
                        QueryList(scope, output);
                    }
                    return;
                }

                //store current item id in view state
                this.ViewState[EntreeServer.Module.ActionTray.ScopeProperties.SharePoint.SPListItemID] = item.ID;

                //render item details
                var group = output.AddListGroup();
                foreach (string viewField in view.ViewFields)
                {
                    var field = list.Fields.GetFieldByInternalName(viewField);
                    if (field.Hidden) continue;
                    var li = group.AddListItem<EntreeListItemDetailNameValue>();
                    li.Name = field.Title;
                    li.Value = field.GetFieldValueAsText(item[field.Id]);
                }

                //add back button for single panel
                if (output.PanelCount < 2)
                {
                    var backAction = new BackActionItem
                    {
                        ClickAction = new EntreeClickActionObject(this)
                        {
                            AnimationStyle = EntreeAnimationStyles.SLIDE_RIGHT
                        }
                    };
                    backAction.ClickAction.Properties[PropertyKeyType] = ScopeTypeReloadApplication;
                    output.HomeBar.ActionItems.Items.Add(backAction);
                }

                //set action scope properties, use SPListItem_Details ScopeType to pull in actions that support this scope i.e. 'Edit', 'Add Attachment', etc.
                setActionScopeProperties(output.ActionScope, EntreeServer.Module.ActionTray.ScopeTypes.SharePoint.SPListItem_Details, item.ID);
            }
        }

        public override void ActiveUpdate(EntreeScopeObject scope, EntreeListItemCollection output)
        {
            //get query text
            var searchQuery = scope.FormProperties[PropertyKeySearchQuery];
            //store query in view state
            this.ViewState[PropertyKeySearchQuery] = searchQuery;
            //remove current selected item
            this.ViewState.Remove(EntreeServer.Module.ActionTray.ScopeProperties.SharePoint.SPListItemID);

            //render results using current search query
            renderResults(output, searchQuery, 0, output.PanelCount > 1);
        }

        public override bool BuildPanelsByScope(EntreeScopeObject currentScope, EntreeScopeCollection panelScopes, PanelDirection direction, ActionScopeObject actionScope)
        {
            //do not build out panels for these conditions - only necessary when transfering control back from an action item
            if (actionScope == null || !actionScope.TransferToApplication || direction == PanelDirection.BACK || !currentScope.Properties.Contains(PropertyKeyType,ScopeTypeRenderItemDetails)) return false;
            
            //in FORWARD direction the object passed is as 'currentScope' will end up with the lowest PanelIndex (0 indicating left most panel)
            //objects in the 'panelScopes' collection will have panel index of it's index of n+1 where n is the index in 'panelScopes'
            //'actionScope' will contain the values set by the ActionProvider is transfering
            currentScope.Properties.CopyTo(panelScopes[0].Properties);
            currentScope.Properties[PropertyKeyType] = ScopeTypeReloadApplication;

            return true;
        }

        public override EntreeConfigPartCollection CreateConfigParts()
        {
            //create and return the custom configpart
            return new EntreeConfigPartCollection
            {
                new ListSearchAppConfigPart(this)
            };
        }
        #endregion

        #region public properties
        /// <summary>
        /// Property to hold Config class. Public properties are propagated to the SP database when an instance of this applicaltion is saved
        /// </summary>
        public ListSearchAppConfig Config { get; set; }
        #endregion

        #region private methods
        private SPQuery generateQuery(string queryText)
        {
            //get the list and view using values from the config
            var list = SPContext.Current.Web.Lists[this.Config.ListId];
            var view = list.Views[this.Config.ViewId];

            var sb = new StringBuilder("<Where>");

            switch (this.Config.FieldInternalNames.Count)
            {
                case 1:
                    sb.AppendFormat(@"<Contains><FieldRef Name=""{0}"" /><Value Type=""Text"">{1}</Value></Contains></Where>", this.Config.FieldInternalNames.First(), queryText);
                    break;
                default:

                    var orClosers = new List<string>
                    {
                        "</Or>"
                    };
                    for (var i = 0; i < this.Config.FieldInternalNames.Count - 2; i++)
                    {
                        sb.AppendFormat(@"<Or><Contains><FieldRef Name=""{0}"" /><Value Type=""Text"">{1}</Value></Contains>", this.Config.FieldInternalNames[i], queryText);
                        orClosers.Add("</Or>");
                    }
                    
                    sb.Append(@"<Or><Contains><FieldRef Name=""{0}"" /><Value Type=""Text"">{1}</Value></Contains><Contains><FieldRef Name=""{2}"" /><Value Type=""Text"">{1}</Value></Contains>".FormatString(this.Config.FieldInternalNames[this.Config.FieldInternalNames.Count-2], queryText,this.Config.FieldInternalNames.Last()));
                    sb.Append(string.Join("", orClosers.ToArray()));
                    sb.Append("</Where>");
                    break;
            }

            //create the query using the generated CAML and the fields from SPView
            return new SPQuery()
            {
                Query = sb.ToString(),
                ViewFields = view.ViewFields.Cast<string>().Aggregate(string.Empty, (sum, value) => sum += @"<FieldRef Name=""{0}"" />".FormatString(value))
            };
        }

        private void renderResults(IEntreeListItemContainer container, string searchQuery, int selectedItemId, bool selectItem)
        {
            //get the list using values from the config
            var list = SPContext.Current.Web.Lists[this.Config.ListId];

            //perform query on list
            try
            {
                //generate SPQuery
                var query = generateQuery(searchQuery);
                //get result set using query
                var results = list.GetItems(query);
                //render results
                foreach (SPListItem item in results)
                {
                    var li = container.AddListItem<EntreeListItemTitle>();
                    li.Title = item.Title;
                    //set selected item if it exists
                    if (item.ID == selectedItemId && selectItem) li["Selected"] = true.ToString().ToLower();
                    li.ClickAction = new EntreeClickActionObject(this);
                    li.ClickAction.Properties[EntreeServer.Module.ActionTray.ScopeProperties.SharePoint.SPListItemID] = item.ID.ToString();
                    li.ClickAction.Properties[PropertyKeyType] = ScopeTypeRenderItemDetails;
                }
            }
            catch(SPException ex)
            {
                Log.Error(ex);
            }
        }

        private void setActionScopeProperties(ActionScopeObject scope, string scopeType, int spListItemId)
        {
            //set ScopeType and appropriate properties to enable action integration
            scope.ScopeType = scopeType;
            scope.Properties[EntreeServer.Module.ActionTray.ScopeProperties.SharePoint.SPListID] = this.Config.ListId.ToString();
            scope.Properties[EntreeServer.Module.ActionTray.ScopeProperties.SharePoint.SPViewID] = this.Config.ViewId.ToString();
            if (spListItemId > 0) scope.Properties[EntreeServer.Module.ActionTray.ScopeProperties.SharePoint.SPListItemID] = spListItemId.ToString();
        }
        #endregion
    }
}
